import 'dart:convert';

import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart' as Get;

import 'package:dio/dio.dart';
import 'package:zootix/services/zt_translate.service.dart';
import 'package:zootix/util/zt_logger_util.dart';
import 'package:zootix/zt_config.dart';
import 'package:zootix/services/zt_storage.service.dart';
import 'package:zootix/store/zt_auth.store.dart';
import 'package:zootix/util/zt_snack_bar.util.dart';

import '../constants/zt_constants.dart';

class ZtDioService extends Get.GetxService {
  static ZtDioService get to => Get.Get.find();
  static Dio get dio => _dio;
  static late Dio _dio;
  List<String> whiteList = [ZtUrls.googleTranslate];
  BaseOptions dioBaseOptions = BaseOptions(
      connectTimeout: const Duration(minutes: 10),
      baseUrl: '${ZtGloablConfig.BASE_URL}',
      followRedirects: true);

  late CancelToken cancelToken;

  /// Initialize the dio instance
  Future<ZtDioService> init() async {
    _dio = Dio(dioBaseOptions);
    _dio.interceptors.add(InterceptorsWrapper(
        onRequest: onRequest, onResponse: onResponse, onError: onError));
    cancelToken = CancelToken();
    return this;
  }

  void onError(DioException e, ErrorInterceptorHandler handler) async {
    if (whiteList.contains(e.requestOptions.path)) {
      await ZtSnackBarUtil().error(e.message);
      return handler.next(e);
    }
    if (ZtGloablConfig.DEBUG) {
      ZtLoggerUtil().info(
          "[Service-dio] Error found on ${e.requestOptions.method} ${e.requestOptions.uri}");
    }
    if (e.type == DioExceptionType.connectionTimeout) {
      // It occurs when url is opened timeout.
      await ZtSnackBarUtil().error(
        'The server is busy, please try again.',
      );
    } else if (e.type == DioExceptionType.sendTimeout) {
      // It occurs when url is sent timeout.
      // await ZtSnackBarUtil().error(
      //   'The server is busy, please try again.',
      // );
    } else if (e.type == DioExceptionType.receiveTimeout) {
      //It occurs when receiving timeout
      // await ZtSnackBarUtil().error(
      //   'The server is busy, please try again.',
      // );
    } else if (e.type == DioErrorType.badResponse) {
      // When the server response, but with a incorrect status, such as 404, 503...
      if (e.response?.statusCode == 400) {
        await ZtSnackBarUtil().error(
          e.response?.data['message'],
        );
      } else if (e.response?.statusCode == 401) {
        await ZtSnackBarUtil().error(
          'Your role at this season has been updated. Please log in again.',
        );
        // dio.lock();
        ZtLoggerUtil().error('[Service-dio] logout:${e.response}');
        await ZtAuthStore().logout(force: true);
      } else if (e.response?.statusCode == 403) {
        await ZtSnackBarUtil().error(
          '${e.response?.data['message']}.',
        );
        // await ZtAuthStore().logout(force: true);
      } else if (e.response?.statusCode == 409) {
        await ZtSnackBarUtil().error(e.response?.data['message']);
      } else if (e.response?.statusCode == 500) {
        await ZtSnackBarUtil()
            .error('The server is busy, please try again later');
      } else if (e.response?.statusCode == 404) {
        ZtLoggerUtil().error('[Service-dio] logout:${e.response}');
        // await ZtAuthStore().logout(force: true);
      } else if (e.response?.statusCode == 422) {
        await ZtSnackBarUtil().error(e.response?.data['message']);
      }
    } else if (e.type == DioErrorType.cancel) {
      // When the request is cancelled, dio will throw a error with this type.
      ZtLoggerUtil().error("[Service-dio] 请求取消");
    } else {
      //DEFAULT Default error type, Some other Error. In this case, you can read the DioError.error if it is not null.
      ZtLoggerUtil().error("[Service-dio] 未知错误:$e");
      if ((e.error?.toString() ?? '')
          .toString()
          .contains("HandshakeException")) {
        // Just a network failure
      } else {
        await ZtSnackBarUtil().error(e.error.toString());
        // ZtAuthStore().logout(force: true);
      }
    }
    return handler.next(e);
  }

  whiteListFilter(String url) => url.endsWith('_allow_anonymous=true');

  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (!whiteList.contains(options.path)) {
      String? token =
          ZtStorageService.to.getString(ZtCacheKeys.token, isWithUser: false);
      if (options.headers['Authorization'] == null && token != null) {
        options.headers["Authorization"] = "Bearer$token";
      }

      options.headers['device-id'] = ZtStorageService.to.getString(
          ZtCacheKeys.deviceUUID,
          isWithUser: false); // 设备id或者udid，保证当前设备唯一值。
      options.headers["platform"] = "iOS";
      options.headers['model'] = ZtStorageService.to
          .getString(ZtCacheKeys.deviceModel, isWithUser: false); // 设备型号
      options.headers['pkg'] = ZtStorageService.to
          .getString(ZtCacheKeys.packageName, isWithUser: false); // 包名
      options.headers['ver'] =
          ZtStorageService.to.getString(ZtCacheKeys.ver, isWithUser: false) ??
              0;
      // ZtStorageService.to.getString('ver') ?? 0; // 版本 例如1.0.0，读取实际配置的版本号
      options.headers['p_ver'] =
          ZtStorageService.to.getString(ZtCacheKeys.pVer, isWithUser: false) ??
              1;
      options.headers['kst'] = '1';
      options.headers['sys_lan'] =
          ZtTranslateService.to.languageCode.value; // 手机系统语言 如英文en 中文zh
    }
    if (ZtGloablConfig.DEBUG && (options.data is! FormData)) {
      ZtLoggerUtil().info('[Service-dio] url: ${options.path}' +
          ', params: ${jsonEncode(options.queryParameters)}' +
          ', body: ${jsonEncode(options.data)}, Header:${jsonEncode(options.headers)}');
    }

    handler.next(options);
  }

  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    /* ZtLoggerUtil().info('[Service-dio]  ${response.data}'); */
    if (whiteList.contains(response.requestOptions.path)) {
      handler.next(response);
      return;
    }
    if (response.data != null) {
      try {
        dynamic responseData = response.data;
        if (response.data is String) {
          responseData = jsonDecode(response.data);
        }
        switch (responseData['code']) {
          case 0:
            handler.next(response);
            return;
          // 其他设备登录
          case 10010304:
            await ZtAuthStore().logout(force: true);
            await ZtSnackBarUtil()
                .error('Other devices have logged in, please log in again.');
            break;
          case 100103:
          case 10010303:
            await ZtAuthStore().logout(force: true);
            await ZtSnackBarUtil()
                .error('Login has timed out, please log in again.');
            break;
          case 10010301:
            await ZtAuthStore().logout(force: true);
            await ZtSnackBarUtil().error('Token can not empty');
            break;
          default:
            await ZtSnackBarUtil()
                .error('${responseData['key']}: ${responseData['msg']}');
            break;
        }
      } catch (e) {
        printError(info: e.toString());
      }
    }
    handler.next(response);
  }
}
